package org.kdev.wtf;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.kdev.wtf.adapter.BaseAdapter;
import org.openqa.selenium.server.RemoteControlConfiguration;
import org.openqa.selenium.server.SeleniumServer;
import org.testng.Assert;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;

public class WtfManager {
	/**
	 * Both proxies use the same selenium connection.
	 */
	public static BaseAdapter adapter = null;

	private static ConfigReader config = null;
	private SeleniumServer seleniumServer = null;
	private static Class aplicationClass = null;
	private static final String startAppMethodName = "startApp";
	private static final String hasLogLineErrorMethodName = "hasLogLineError";
	private LogFileWatcher logFileWatcher = null;

	private void readConfig(String fileName) {
		try {
			config = new ConfigReader(fileName);
		} catch (IOException e) {
			Assert.fail("Can not read config file: " + fileName);
		}
	}

	private void initAppManagerClass() {
		try {
			aplicationClass = Class.forName(config.getAppManagerClass());
		} catch (ClassNotFoundException e) {
			Assert.fail("AppManager class not found! \n" + e.getMessage());
		}
	}

	private void initLogWatcher() {

		Class paramTypes = String.class;
		Method logonMethod = null;
		try {
			logonMethod = aplicationClass.getMethod(hasLogLineErrorMethodName,
					paramTypes);
		} catch (NoSuchMethodException e) {
			Assert.fail("AppManager method missing: "
					+ hasLogLineErrorMethodName + " \n");
		}
		String logFileName = config.getLogFilePath().trim();
		File logFile = new File(logFileName);

		if (!logFile.exists()) {
			System.out.println("LogFile: None found, creating in '"
					+ logFileName + "'...");
			try {
				logFile.createNewFile();
			} catch (IOException e) {
				System.out.println("!!! Can't create LogFile in '"
						+ logFileName + "'\n");
				// e.printStackTrace();
			}
		}
		if (logFile.exists() && logFile.canWrite()) {
			System.out.println("LogFile: '" + logFileName + "'");
			logFileWatcher = new LogFileWatcher(logFile, 2000, logonMethod);
		} else {
			System.out.println("!!! LogFile: '" + logFileName
					+ "' does not exist or can't be modified\n");
			Assert.fail("!!! LogFile: '" + logFileName
					+ "' does not exist or can't be modified\n");
		}

	}

	@BeforeSuite(alwaysRun = true)
	@Parameters( { "config-file" })
	public void startSeleniumServer(String fileName) throws Exception {
		// initialize
		readConfig(fileName);
		initAppManagerClass();

		// starting
		System.out.println("Starting Selenium Server ...");
		RemoteControlConfiguration seleniumServerConfig = new RemoteControlConfiguration();

		seleniumServerConfig.setFirefoxProfileTemplate(new File(config
				.getBrowserProfileTemplate()));

		seleniumServerConfig.setPort(config.getSeleniumServerPort());
		seleniumServer = new SeleniumServer(seleniumServerConfig);
		seleniumServer.start();
		System.out.println("Started Selenium Server");
	}

	@AfterSuite(alwaysRun = true)
	public void stopSeleniumServer() throws Exception {
		System.out.println("Stopping Selenium Server ...");
		seleniumServer.stop();

		/*
		 * XXX selenium 1.0.3 nie zamyka okna serwera dlatego na koniec ubijamy
		 * procesy firefoxa
		 */
		// Runtime r = Runtime.getRuntime();
		// Process p = r.exec("killall firefox-bin");
		// p.waitFor();
		// p.destroy();

		System.out.println("Stopped Selenium Server");

		aplicationClass = null;
	}

	private void stopLogWatcher() {
		if (logFileWatcher != null) {
			logFileWatcher.stopWatching();

			if (logFileWatcher.hasLogError())
				Assert.fail("Errors in log file");

			logFileWatcher = null;
		}
	}

	/**
	 * Starting application using login and password or abandon.
	 * 
	 * @param login
	 * @param pass
	 */
	private void startApplication(String login, String pass) {
		if (aplicationClass == null)
			return;

		Class[] paramTypes = { String.class, String.class };
		try {
			// zaloguj do systemu
			Method logonMethod = aplicationClass.getMethod(startAppMethodName,
					paramTypes);
			Object[] parameters = { login, pass };
			logonMethod.invoke(null, parameters);
		} catch (NoSuchMethodException e) {
			Assert.fail("There is no method 'startApp' in AppManagerClass ! \n"
					+ e.getMessage());
		} catch (InvocationTargetException e) {
			Assert.fail("AppManagerClass.startApp() calling failed !\n"
					+ e.getTargetException().getMessage());
		} catch (Exception e) {
			Assert.fail("AppManagerClass.startApp() calling failed !\n"
					+ e.getMessage());
		}

	}

	@BeforeTest(alwaysRun = true)
	@Parameters( { "login", "pass" })
	public void startSeleniumClient(@Optional("") String login,
			@Optional("") String pass) throws IllegalArgumentException,
			SecurityException, IllegalAccessException, NoSuchFieldException {

		adapter = new BaseAdapter(config.getSeleniumServerHost(), config
				.getSeleniumServerPort(), config.getBrowserStartCommand(),
				config.getAppServerUrl(), config.getAppContext(), config
						.getTimeout(), aplicationClass, config
						.getDownloadPath());

		// maximize window
		adapter.start();
		adapter.setTimeout(config.getTimeout());
		adapter.windowMaximize();

		// start watchig log file
		initLogWatcher();

		// startup application
		startApplication(login, pass);
	}

	@AfterTest(alwaysRun = true)
	public void stopSeleniumClient() throws Exception {
		adapter.close();
		adapter.stop();
		adapter = null;

		stopLogWatcher();
	}
}
